package com.zndroid.nfc;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.MifareUltralight;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcB;
import android.nfc.tech.NfcF;
import android.nfc.tech.NfcV;
import android.provider.Settings;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.zndroid.nfc.core.Delegate;
import com.zndroid.nfc.core.ICommonFun;
import com.zndroid.nfc.core.IProtocol;
import com.zndroid.nfc.core.StringUtil;
import com.zndroid.nfc.core.impl.DefaultProtocol;

/**
 * Created by lzy on 2021/9/8.
 *
 * NFC英文全称Near Field Communication，近距离无线通信(需要距离为4cm或更短来初始化一个连接)
 *
 * NDEF(NFC data exchange format)
 *
 * permission allow by <code>android.permission.NFC</code>
 */
public final class NFC
        implements ICommonFun
{
    /**
     * 只要当没有安装其他的应用程序来处理ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED intent的情况下，
     * 才会使用ACTION_TAG_DISCOVERED来作为最后的过滤应用程序的依据。
     *
     * 建议启动模式<code>android:launchMode="singleTop"</code> 或者 <code>android:launchMode="singleTask"</code>，
     * 然后在对应生命周期中调用对应的方法
     *
     * 三种过滤器处理优先级：NDEF > TECH > TAG

     * 为了过滤ACTION_NDEF_DISCOVERED intents，则声明包含有你想要过滤的数据的类型的intent filter,比如：
     * 样例1.
     * <code>
     *     <intent-filter>
     *          <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
     *          <category android:name="android.intent.category.DEFAULT"/>
     *          <data android:mimeType="text/plain" />
     * </intent-filter>
     * </code>
     *
     * 样例2.(下面的例子过滤一个形为http://developer.android.com/index.html的URI)
     * <code>
     *     <intent-filter>
     *          <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
     *          <category android:name="android.intent.category.DEFAULT"/>
     *          <data android:scheme="http"
     *               android:host="developer.android.com"
     *               android:pathPrefix="/index.html" />
     * </intent-filter>
     * </code>
     *
     * 如果你的activity过滤 ACTION_TECH_DISCOVERED intent，则你必须创建一个XML资源文件在一个tech-list集合中描述你的activity支持的技术。
     * 如果一个tech-list集合是标签支持的技术的子集，则你的activity将被认为是一个匹配，你可以通过调用getTechList()来获取标签支持的技术。
     * */
    private final int REQUEST_CODE = 99;

    private final String ACTION_NDEF_DISCOVERED = NfcAdapter.ACTION_NDEF_DISCOVERED;
    private final String ACTION_TECH_DISCOVERED = NfcAdapter.ACTION_TECH_DISCOVERED;
    private final String ACTION_TAG_DISCOVERED = NfcAdapter.ACTION_TAG_DISCOVERED;

    private PendingIntent nfcPendingIntent;
    private IntentFilter[] intentFilters = new IntentFilter[3];
    private String[][] techListArray;

    private NfcAdapter nfcAdapter;
    private Intent intent;

    private INFC listener;
    private IAdapter adapter;

    private Delegate delegate;

    public void setAdapter(IAdapter adapter) {
        this.adapter = adapter;
    }

    public void init(Context context, @NonNull INFC listener, @Nullable IProtocol protocol) {
        this.nfcAdapter = NfcAdapter.getDefaultAdapter(context);
        this.listener = listener;
        this.listener.onSupport(isSupport());

        if (null == protocol) {
            this.delegate = new Delegate(new DefaultProtocol());
        } else {
            this.delegate = new Delegate(protocol);
        }

        if (isSupport()) {
            logI("support NFC = ", String.valueOf(true));

            initParams(context);
        } else {
            logW("support NFC = ", String.valueOf(false));
        }
    }

    public void init(Context context, @NonNull INFC listener) {
        this.init(context, listener, null);
    }

    private void initParams(Context context) {
        Intent i = new Intent(context, context.getClass());
        nfcPendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT);

        IntentFilter filter0 = new IntentFilter(ACTION_NDEF_DISCOVERED);
        filter0.addCategory(Intent.CATEGORY_DEFAULT);
        try {
            filter0.addDataType("text/plain");
        } catch (IntentFilter.MalformedMimeTypeException e) {
            e.printStackTrace();
        }

        IntentFilter filter1 = new IntentFilter(ACTION_TAG_DISCOVERED);
        filter1.addCategory(Intent.CATEGORY_DEFAULT);
        IntentFilter filter2 = new IntentFilter(ACTION_TECH_DISCOVERED);
        filter2.addCategory(Intent.CATEGORY_DEFAULT);

        intentFilters[0] = filter0;
        intentFilters[1] = filter1;
        intentFilters[2] = filter2;

        techListArray = new String[][] {
                new String[] { NfcF.class.getName() },
                new String[] { NfcA.class.getName() },
                new String[] { NfcB.class.getName() },
                new String[] { NfcV.class.getName() },
                new String[] { Ndef.class.getName() },
                new String[] { NdefFormatable.class.getName() },
                new String[] { IsoDep.class.getName() },
                new String[] { MifareClassic.class.getName() },
                new String[] { MifareUltralight.class.getName() }
        };

        if (context instanceof Activity) {
            onNewIntent(((Activity) context).getIntent());
        }
    }

    private boolean isSupport() {
        return nfcAdapter.isEnabled();
    }

    public String[] getTechList() {
        if (null == intent || null == nfcAdapter) return new String[0];

        if (ACTION_TECH_DISCOVERED.equalsIgnoreCase(intent.getAction())) {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            return (null == tag) ? new String[0] : tag.getTechList();
        } else {
            return new String[0];
        }
    }

    public void openNFCSetting(Context context) {
        context.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
    }

    @Override
    public void onNewIntent(Intent intent) {
        this.intent = intent;
        previewDeal(this.intent, this.listener);
    }

    @Override
    public void onResume(Activity yourActivity) {
        nfcAdapter.enableForegroundDispatch(yourActivity, nfcPendingIntent, intentFilters, techListArray);
    }

    @Override
    public void onPause(Activity yourActivity) {
        nfcAdapter.disableForegroundDispatch(yourActivity);
    }

    private void previewDeal(Intent intent, INFC listener) {
        String action = intent.getAction();
        logI("getAction = ", action);

        if (TextUtils.isEmpty(action)) return;

        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String id = "";

        if (null != tag) {
            id = StringUtil.byte2HexString(tag.getId());
        }

        if (ACTION_TECH_DISCOVERED.equalsIgnoreCase(action)) {
            logI("induction devices is valid = ", id);
            listener.onValid(id);

            ////////tech list
            {
                StringBuilder sb = new StringBuilder("[\n");
                for (String it : getTechList()) {
                    sb.append(it).append("\n");
                }

                sb.append("]");

                logI("it support tech is = ", sb.toString());
            }
            ////////tech list

            ////
            delegate.consume(Delegate.TECH, intent, adapter);

        } else if (ACTION_NDEF_DISCOVERED.equalsIgnoreCase(action)) {
            logI("induction devices is valid = ", id);
            listener.onValid(id);

            ////
            delegate.consume(Delegate.NDEF, intent, adapter);
        } else if (ACTION_TAG_DISCOVERED.equalsIgnoreCase(action)) {
            logI("induction devices is valid = ", id);
            listener.onValid(id);

            ////
            delegate.consume(Delegate.TAG, intent, adapter);
        } else {
            logE("induction devices is invalid", "no 'SN' id");
            listener.onInvalid();
        }
    }

    //////////////////////////////////////////////////////////////////////////
    private NFC(){}

    private static final class N {
        private static final NFC FC = new NFC();
    }

    public static NFC getInstance() {
        return N.FC;
    }

    public void logI(String key, String value) {
        key = ">>> " + key;

        SpannableString spannableString = new SpannableString(key + "\n" + value);
        spannableString.setSpan(new ForegroundColorSpan(Color.parseColor("#17BB56")), key.length() + 1, spannableString.length(), SpannableString.SPAN_INCLUSIVE_INCLUSIVE);
        if (null != listener) {
            listener.console(spannableString);
        }
    }

    public void logW(String key, String value) {
        key = ">>> " + key;

        SpannableString spannableString = new SpannableString(key + "\n" + value);
        spannableString.setSpan(new ForegroundColorSpan(Color.parseColor("#B5BB15")), key.length() + 1, spannableString.length(), SpannableString.SPAN_INCLUSIVE_INCLUSIVE);
        if (null != listener) {
            listener.console(spannableString);
        }
    }

    public void logE(String key, String value) {
        key = ">>> " + key;

        SpannableString spannableString = new SpannableString(key + "\n" + value);
        spannableString.setSpan(new ForegroundColorSpan(Color.parseColor("#FF6B68")), key.length() + 1, spannableString.length(), SpannableString.SPAN_INCLUSIVE_INCLUSIVE);
        if (null != listener) {
            listener.console(spannableString);
        }
    }
}
